﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

using Crank.Core.Extensions;

namespace Crank.Core
{
    public abstract class TokenizerBase : ITokenProvider
    {
        #region ITokenProvider Members

        public bool HasTokens
        {
            get;
            protected set;
        }

        public Token CurrentToken
        {
            get;
            protected set;
        }

        public virtual Token Eat(TokenType expected)
        {
            if (CurrentToken.Type != expected)
                throw new Exception("Unexpected token Provided");
            Token returnedToken = CurrentToken;
            LoadNextToken();
            return returnedToken;
        }

        public bool TryEat(TokenType expected)
        {
            if (CurrentToken.Type != expected)
                return false;
            Eat(expected);
            return true;
        }

        #endregion

        #region Token Recognizers

        protected IList<Recognizer> Recognizers;

        protected class Recognizer
        {
            public readonly Regex Regex;
            public readonly TokenType Type;

            public Recognizer(Regex regex, TokenType type)
            {
                Regex = regex;
                Type = type;
            }
        }

        public TokenizerBase()
        {
            Recognizers = new List<Recognizer>();
        }

        protected void Recognize(string text, TokenType type)
        {
            Recognizers.Add(new Recognizer(new Regex(text), type));
        }

        #endregion

        protected ILineProvider source;

        string current_line;

        protected virtual void LoadNextToken()
        {
            do
            {
                current_line = current_line ?? source.GetNextLine();

                if (current_line == null)
                {
                    HasTokens = false;
                    CurrentToken = Token.EOF;
                    return;
                }

                current_line = current_line.Trim();
            } while (current_line.IsOnlyWhitespace());

            CurrentToken = Token.UNKNOWN;

            foreach (Recognizer rec in Recognizers)
            {
                if (rec.Regex.IsMatch(current_line))
                {
                    Match match = rec.Regex.Match(current_line);
                    if (match != Match.Empty)
                    {
                        CurrentToken = new Token(match.Value, rec.Type);
                        current_line = rec.Regex.Replace(current_line, String.Empty);
                        break;
                    }
                }
            }

            if (CurrentToken.Is(TokenType.Unknown))
                throw new Exception("Unexpected Token Encountered");

            if (current_line.IsOnlyWhitespace())
                current_line = null;

            HasTokens = source.HasLines || current_line != null;
        }
    }
}
